package com.sl.core.domain.dto;

import com.fasterxml.jackson.core.type.TypeReference;
import com.sl.core.utils.JsonUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.BufferedReader;
import java.io.StringReader;
import java.util.*;

@Getter
@Setter
public class SquareLakeLayout {

    private Transformation transformation;

    private static final Map<String, String> KEY_MAPPING = new HashMap<>();

    static {
        KEY_MAPPING.put("transformation", "pipeline");
        KEY_MAPPING.put("step", "transform");
        KEY_MAPPING.put("step_error_handling", "transform_error_handling");
        KEY_MAPPING.put("trans_type", "pipeline_type");
        KEY_MAPPING.put("capture_step_performance", "capture_transform_performance");
        KEY_MAPPING.put("step_performance_capturing_delay", "transform_performance_capturing_delay");
        KEY_MAPPING.put("step_performance_capturing_size_limit", "transform_performance_capturing_size_limit");
        KEY_MAPPING.put("trans-log-table", "pipeline-log-table");
        KEY_MAPPING.put("step-log-table", "transform-log-table");


    }

    public static SquareLakeLayout basic(String name, String description) {
        return SquareLakeLayout.basic(name, description, "Normal");
    }

    public static SquareLakeLayout basic(String name, String description, String transType) {
        SquareLakeLayout squareLakeLayout = new SquareLakeLayout();
        Transformation basic = Transformation.basic(name, description, transType);
        squareLakeLayout.setTransformation(basic);
        return squareLakeLayout;
    }

    public static SquareLakeLayout loadMap(Map<String, Object> map) {
        SquareLakeLayout squareLakeLayout = JsonUtil.toBean(map, SquareLakeLayout.class);
        squareLakeLayout.getTransformation().rebuild();
        return squareLakeLayout;
    }

    public String toXml() {
        StringBuilder buffer = new StringBuilder();
        buffer.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");

        TransformationStep.build(this.transformation.getStep());

        String s = JsonUtil.toStr(this);

        Map<String, Object> jObj = JsonUtil.toBean(s, new TypeReference<Map<String, Object>>() {
        });
        mapToXml(jObj, buffer);

        return buffer.toString();
    }

    /**
     * XML字符串转JSON对象
     *
     * @param xml xml字符串
     * @return JSON对象
     * @throws DocumentException
     */
    @SneakyThrows
    public String toJson(String xml) {
        Map<String, Object> json = new HashMap<>();
        SAXReader reader = new SAXReader();
        Document document = reader.read(new BufferedReader(new StringReader(xml)));
        Element root = document.getRootElement();
        json.put(root.getName(), elementToJson(root));
        return json.toString();
    }


    @SneakyThrows
    public String toJson() {
        String xml = this.toXml();
        Map<String, Object> json = new HashMap<>();
        SAXReader reader = new SAXReader();
        Document document = reader.read(new BufferedReader(new StringReader(xml)));
        Element root = document.getRootElement();
        json.put(root.getName(), elementToJson(root));
        return json.toString();
    }

    /**
     * Element对象转JSON对象
     *
     * @param element Element对象
     * @return JSON对象
     */
    private static Object elementToJson(Element element) {
        Map<String, Object> json = new HashMap<>();
        Map<String, List> hm = new HashMap<>();
        for (Object child : element.elements()) {
            Element e = (Element) child;
            boolean isArray = isArray(e);
            if (isArray) {
                List array;
                array = hm.get(e.getName());
                if (array == null) {
                    array = new ArrayList();
                    hm.put(e.getName(), array);
                    json.put(e.getName(), array);
                }
                if (e.elements().isEmpty()) {
                    array.add(e.getText());
                } else {
                    array.add(elementToJson(e));
                }
            } else {
                if (e.elements().isEmpty()) {
                    json.put(e.getName(), e.getText());
                } else {
                    json.put(e.getName(), elementToJson(e));
                }
            }
        }
        return json;
    }


    private static boolean isArray(Element elements) {
        String array = elements.attributeValue("array");
        if (array == null) {
            return false;
        }
        return array.equals("true");
    }

    private static String mapToXml(Map<String, Object> jObj, StringBuilder buffer) {
        Set<Map.Entry<String, Object>> se = jObj.entrySet();
        for (Iterator<Map.Entry<String, Object>> it = se.iterator(); it.hasNext(); ) {
            Map.Entry<String, Object> en = it.next();
            if (en.getValue() instanceof Map) {
                Map jo = (Map) jObj.get(en.getKey());
                buffer.append("<" + nameMapping(en.getKey()) + ">");
                mapToXml(jo, buffer);
                buffer.append("</" + nameMapping(en.getKey()) + ">");
            } else if (en.getValue() instanceof Collection<?>) {
                List jarray = (List) jObj.get(en.getKey());
                for (int i = 0; i < jarray.size(); i++) {
                    buffer.append("<" + nameMapping(en.getKey()) + " array=\"true\">");
                    Object obj = jarray.get(i);
                    if (obj instanceof Map) {
                        mapToXml((Map) obj, buffer);
                    } else {
                        buffer.append(initContent(obj));
                    }
                    buffer.append("</" + nameMapping(en.getKey()) + ">");
                }
            } else {
                buffer.append("<" + nameMapping(en.getKey()) + ">" + initContent(en.getValue()));
                buffer.append("</" + nameMapping(en.getKey()) + ">");
            }
        }
        return buffer.toString();
    }
    private  static String nameMapping(String name){
        String s = KEY_MAPPING.get(name);
        if(s==null||"".equals(s)){
            return name;
        }
        return s;
    }

    private static String initContent(Object content) {
        if (content == null) {
            return "";
        }
        if ("".equals(content.toString())) {
            return "";
        }
        return "<![CDATA[" + content + "]]>";
    }
}
